use crate::core::c::URL_SEPARATOR;
use crate::core::error::ParrotError;
use crate::core::step_context::StepContext;
use directories::{ProjectDirs, UserDirs};
use lazy_static::lazy_static;
use std::sync::Arc;
use time::Duration;
use tokio::io::{AsyncBufReadExt, AsyncRead, BufReader};
use tokio::sync::oneshot::Receiver;
use tokio::time;

pub struct Util {}

const QUALIFIER: &str = "com";
const ORGANIZATION: &str = "beargod";
const APP_NAME: &str = "parrot";

lazy_static! {
    static ref APP_DIRS: ProjectDirs =
        ProjectDirs::from(QUALIFIER, ORGANIZATION, APP_NAME).expect("Can not get app directories");
    static ref USER_DIRS: UserDirs = UserDirs::new().expect("Can not get user directories");
}

impl Util {
    pub fn app_name() -> String {
        APP_NAME.to_string()
    }
    pub fn app_dirs() -> &'static ProjectDirs {
        &(APP_DIRS)
    }

    pub fn user_dirs() -> &'static UserDirs {
        &(USER_DIRS)
    }

    pub fn log<S: AsRef<str> + std::fmt::Debug>(s: S) {
        println!("{s:?}");
    }
    pub fn log_error<S: AsRef<str> + std::fmt::Debug>(s: S) {
        eprintln!("{s:?}");
    }

    pub fn log_opt_err(opt_err: &ParrotError) {
        eprintln!("{}", opt_err.to_string())
    }

    pub fn get_git_repo_name(repo_url: &str) -> Option<String> {
        let start = repo_url.rfind(URL_SEPARATOR)? + 1;
        let end = repo_url.rfind(".git")?;
        if start < end {
            Some(repo_url[start..end].to_string())
        } else {
            None
        }
    }

    pub async fn log_line_continuously<R: AsyncRead + Unpin>(
        steam: R,
        context: Arc<StepContext>,
        mut receiver: Receiver<()>,
    ) -> () {
        let mut br = BufReader::new(steam);
        let mut buf = String::new();
        while let Ok(n) = br.read_line(&mut buf).await {
            if let Ok(_) = receiver.try_recv() {
                break;
            }

            if n > 0 {
                context.log(&buf);
                buf.clear();
            } else {
                time::sleep(Duration::from_secs(1)).await;
            }
        }
    }
}
